Skip to main content

Chapter 6: Participate in the development of godot open source game

Let’s use Cursor to help Godot game development and experience the charm of AI-assisted development!

1. The Path of Fighter(POF) project

1 Project Overview

First, let's take a look at this open source game project:

Path of Fighter (POF) is a simplified 2D pixel-style action RPG inspired by Path of Exile, with simplified game mechanics, reduced difficulty, and enhanced entertainment value.

📁 Path of Fighter/
├── Game type: 2D action RPG
├── Development engine: Godot 4.0
├── Programming language: C# (Godot Mono)
└── Main features:
- Skill system
- Battle system
- Character growth

2. Development Environment Configuration Guide

🌟 Development environment preparation list

  • Install Git
  • Download GitHub Desktop
  • Install .NET SDK
  • Download Godot Mono version Remember: Tools are just auxiliary, and the important thing is to start working on the project. Don’t worry about tool selection for too long. Use it first, and then upgrade or replace it if necessary!

1 Git - Your code time machine ⏰

Do you remember the pain of trying to restore the previous version but not finding the backup after you modified the code? Git is born to solve this problem!

(1 What is Git?

Imagine that you can save anytime when playing games and go back to any save point - Git is the "archiving system" when writing code!

🎮 Main Functions

  • Code version management (like game archives)
  • Multiplayer collaboration (team into dungeons)
  • Code backup (prevent the computer from blowing)

(2 How to install git tools

Visit https://git-scm.com/downloads

Install the mac platform and execute commands

brew install git

Install the windows platform, access the link to download the installation package https://git-scm.com/downloads/win

2 GitHub Desktop - Point and Shoot Camera in the Code Management World 📸

If Git is a professional camera, GitHub Desktop is a point-and-shoot camera - one-click operation, novices friendly!

(1 Why use it?

  • No need to remember the commands, all depends on the little bit
  • Clear interface, clear at a glance
  • Suitable for beginners

(2 Use three steps

(1. Download and install: Visit GitHub Desktop official website
(2. Log in to your GitHub account
(3. Clone or create a project to get started!

(3 Overall process of how to use GitHub Desktop for game development and management

1. Open GitHub Desktop and log in to your GitHub account
2. Select "File > Clone Repository"
3. Select the POF project https://github.com/aiwalkerstudio/pixpof.git, so that you can clone the project locally
4. Create a new branch: feature/fireball-skill
5. Develop the current branch code //Corresponding to the skill development below
6. Submit the code and create a pull request, push it to the main branch

(4 Download GitHub Desktop

Visit https://desktop.github.com/download/

After the download is completed, install

(5 How to register a GitHub account

View detailed registration documents https://docs.github.com/en/get-started/start-your-journey/creating-an-account-on-github

3.NET SDK - Microsoft's Magic Toolkit 🎁

This is the "engine" of C# language, and it is impossible to move forward without it!

(1 Why do you need it?

  • A must-have for running C# programs
  • Godot Mono version requires
  • Cross-platform development tools

(2 Download .net toolkit

Windows installation

Visit https://dotnet.microsoft.com/download

Click the button “Download .NET SDK”

After the download is completed, install

mac installation

Visit https://dotnet.microsoft.com/download to download and install

or

Open the command line and enter "brew install dotnet@4.6.2"

4 Godot Mono IDE - Swiss Army Knife for Game Development 🎮

Want to play games but don’t know where to start? Godot is your best choice!

(1 Why choose Godot?

  • Free and open source (yes, you read that right, it's free!)
  • Supports 2D and 3D (do whatever you want)
  • Cross-platform (can be played with PC, mobile phone, and web pages)
  • C# blessing (writing code is more comfortable)

(2 Download and install godot mono

Visit https://godotengine.org/download/

Click the button “(macOS) Godot Engine - .NET4.4”

After the download is completed, install

💡 Friendly Tips for Beginners:

  1. Don't be scared by these tools, they are all easy to use
  2. Ask Cursor first when encountering problems
  3. If it really doesn't work, ask AI assistant deepseek, doubao, etc.
  4. Hands-on practice is the most important!

3. Development process

1 Download the pixpof code

Open GitHub Desktop and click the button “File > Clone Repository”

Enter the address https://github.com/aiwalkerstudio/pixpof.git

alt text

Click "Clone" and wait for the project to be copied to complete

2 Create a development branch

Open GitHub Desktop and click the button “Branch > new Branch ..” alt text

alt text

3 Use cursor to open the project folder where pixpof is located

alt text

4 Develop new skills for pixpof: Ice bomb

You: @reqs.md @tech.md Implement an Icebolt skill and bind it to the shortcut key of skill 1

Cursor: Analyze the requirements:

1. A new Icebolt skill needs to be implemented
2. It needs to be bound to the shortcut key of skill 1
3. Need to create:
- Icebolt skill class
- Icebolt projectile class
4. Need to modify:
- SkillSlot.cs to configure the skill
5. Skill features:
- Should be an active skill
- Should inherit ProjectileSkill
- Should have a deceleration effect
- Should have a freezing visual effect

6. Refer to the existing Fireball and Soulrend skills for implementation

Steps:

1. Create an IceBolt skill class
2. Create an IceBoltProjectile class
3. Modify the skill configuration in SkillSlot.cs
4. Make sure the skill is bound to the shortcut key of skill 1 (W key)

...

In this way, the Icebolt skill will be bound to the shortcut key of skill 1 (W key).

Main features:
Frostbomb is an active skill, inherited from ProjectileSkill
Frostbomb has a deceleration effect, which can reduce the enemy's movement speed
Frostbomb has a freezing visual effect, including ice particles and freezing aura
Frostbomb supports multiple projections, and can launch multiple ice bombs at the same time
5. Frostbomb is bound to the shortcut key of skill 1 (W key)

💡 Practical Tips: @reqs.md @tech.md These two files are to let cursor know our needs and technical points, and the functions developed are more in line with our requirements.

💡 Code implementation

// IceBolt.cs
using Godot;
using System.Collections.Generic;
using Game.Skills.Base;
using Game.Enemies;

namespace Game.Skills.Active
{
public partial class IceBolt : ProjectileSkill
{
private bool _isMultishot = false;
private const float PROJECTILE_SPEED = 350f;
private const float DAMAGE = 20f;
private const float SLOW_FACTOR = 0.5f; // 减速效果
private const float SLOW_DURATION = 3.0f; // 减速持续时间
private const int MULTISHOT_COUNT = 3;
private const float MULTISHOT_ANGLE = 15f;

public override string Name { get; protected set; } = "寒冰弹";
public override string Description { get; protected set; } = "发射一枚寒冰弹,对敌人造成伤害并减速";
public override float Cooldown { get; protected set; } = 2.0f;
public override float ManaCost { get; protected set; } = 15.0f;

public override void Initialize()
{
base.Initialize();
GD.Print($"寒冰弹技能初始化完成");
}

public override void EnableMultiProjectiles()
{
_isMultishot = true;
GD.Print("寒冰弹: 启用多重投射模式");
}

public override void DisableMultiProjectiles()
{
_isMultishot = false;
GD.Print("寒冰弹: 关闭多重投射模式");
}

protected override void CreateProjectile(Node2D source)
{
if (_isMultishot)
{
CreateMultipleProjectiles(source);
}
else
{
CreateSingleProjectile(source);
}
}

private void CreateSingleProjectile(Node2D source)
{
var projectile = new IceBoltProjectile();
projectile.Speed = PROJECTILE_SPEED;
projectile.Damage = DAMAGE;
projectile.Direction = GetAimDirection(source);
projectile.Source = source;
projectile.GlobalPosition = source.GlobalPosition;

source.GetTree().CurrentScene.AddChild(projectile);
GD.Print($"寒冰弹发射: 位置={source.GlobalPosition}, 方向={projectile.Direction}");
}

private void CreateMultipleProjectiles(Node2D source)
{
Vector2 baseDirection = GetAimDirection(source);

for (int i = 0; i < MULTISHOT_COUNT; i++)
{
float angle = (i - (MULTISHOT_COUNT - 1) / 2f) * Mathf.DegToRad(MULTISHOT_ANGLE);
Vector2 direction = baseDirection.Rotated(angle);

var projectile = new IceBoltProjectile();
projectile.Speed = PROJECTILE_SPEED;
projectile.Damage = DAMAGE;
projectile.Direction = direction;
projectile.Source = source;
projectile.GlobalPosition = source.GlobalPosition;

source.GetTree().CurrentScene.AddChild(projectile);
}

GD.Print($"多重寒冰弹发射: 位置={source.GlobalPosition}, 数量={MULTISHOT_COUNT}");
}

private partial class IceBoltProjectile : Area2D
{
[Export]
public float Speed { get; set; } = 350.0f;

[Export]
public float Damage { get; set; } = 20.0f;

public Vector2 Direction { get; set; } = Vector2.Right;
public Node Source { get; set; }

private float _lifetime = 5.0f;

public override void _Ready()
{
CollisionLayer = 8; // 第4层,用于投射物
CollisionMask = 4; // 第3层,用于检测敌人

var shape = new CircleShape2D();
shape.Radius = 8f;
var collision = new CollisionShape2D();
collision.Shape = shape;
AddChild(collision);

// 添加视觉效果
var sprite = new ColorRect();
sprite.Color = new Color(0.5f, 0.8f, 1.0f); // 淡蓝色
sprite.Size = new Vector2(16, 16);
sprite.Position = new Vector2(-8, -8);
AddChild(sprite);

// 添加冰晶效果
var iceParticles = new CPUParticles2D();
iceParticles.Amount = 20;
iceParticles.Lifetime = 0.5f;
iceParticles.OneShot = false;
iceParticles.Explosiveness = 0.0f;
iceParticles.Direction = Vector2.Zero;
iceParticles.Spread = 180;
iceParticles.Gravity = Vector2.Zero;
iceParticles.InitialVelocity = 20.0f;
iceParticles.Scale = 2.0f;
iceParticles.ScaleAmount = 1.0f;
iceParticles.Color = new Color(0.7f, 0.9f, 1.0f, 0.7f);
AddChild(iceParticles);

// 添加发光效果
var glow = new ColorRect();
glow.Color = new Color(0.7f, 0.9f, 1.0f, 0.3f); // 淡蓝色发光
glow.Size = new Vector2(24, 24);
glow.Position = new Vector2(-12, -12);
glow.ZIndex = -1;
AddChild(glow);

BodyEntered += OnBodyEntered;
AreaEntered += OnAreaEntered;
}

public override void _Process(double delta)
{
_lifetime -= (float)delta;
if (_lifetime <= 0)
{
QueueFree();
return;
}

Position += Direction * Speed * (float)delta;
}

private void OnBodyEntered(Node2D body)
{
if (body is Monster monster)
{
GD.Print($"寒冰弹击中怪物: {monster.Name}");
monster.TakeDamage(Damage);

// 应用减速效果
ApplySlowEffect(monster);

// 播放冰冻特效
PlayFreezeEffect(monster.GlobalPosition);

QueueFree();
}
else if (body is Enemy enemy)
{
GD.Print($"寒冰弹击中敌人: {enemy.Name}");
enemy.TakeDamage(Damage);

// 播放冰冻特效
PlayFreezeEffect(enemy.GlobalPosition);

QueueFree();
}
}

private void OnAreaEntered(Area2D area)
{
// 处理与其他区域的碰撞
}

private void ApplySlowEffect(Monster monster)
{
// 这里应该调用怪物的减速方法
// 由于Monster类可能没有直接的减速方法,这里只是示例
// 实际实现时需要在Monster类中添加相应的方法
GD.Print($"对怪物 {monster.Name} 施加减速效果: {SLOW_FACTOR * 100}%, 持续 {SLOW_DURATION} 秒");

// 如果Monster有减速方法,可以这样调用:
// monster.ApplySlowEffect(SLOW_FACTOR, SLOW_DURATION);
}

private void PlayFreezeEffect(Vector2 position)
{
// 创建冰冻特效
var freezeEffect = new Node2D();
Source.GetTree().CurrentScene.AddChild(freezeEffect);
freezeEffect.GlobalPosition = position;

// 添加冰晶粒子
var particles = new CPUParticles2D();
particles.Emitting = true;
particles.OneShot = true;
particles.Explosiveness = 1.0f;
particles.Amount = 20;
particles.Lifetime = 0.5f;
particles.Direction = Vector2.Zero;
particles.Spread = 180;
particles.Gravity = Vector2.Zero;
particles.InitialVelocity = 50.0f;
particles.Scale = 3.0f;
particles.Color = new Color(0.7f, 0.9f, 1.0f);
freezeEffect.AddChild(particles);

// 添加冰冻光环
var iceRing = new ColorRect();
iceRing.Color = new Color(0.7f, 0.9f, 1.0f, 0.5f);
iceRing.Size = new Vector2(40, 40);
iceRing.Position = new Vector2(-20, -20);
freezeEffect.AddChild(iceRing);

// 创建消失动画
var tween = freezeEffect.CreateTween();
tween.TweenProperty(iceRing, "modulate:a", 0.0f, 0.5f);
tween.TweenCallback(Callable.From(() => freezeEffect.QueueFree()));
}
}
}
}

5 Use godot mono to open the project and run the game

alt text Click “import existing project”

Navigate to the pixpof project directory and select the project.godot file alt text

alt text Click "Import & Edit"

alt text After opening the project, click on the white triangle in the upper right corner to run the game

alt text

6 Found Godot editor error

alt text CS0246: The type or namespace name 'CPUParticles2D' could not be found (are you missing a using directive or an assembly reference?) /Users/liyihang/Downloads/cursortest/pixpof/src/Skills/Active/IceBolt.cs(120,40)

7 Copy the error message to the cursor chat box

alt text

You:
CS0246: The type or namespace name 'CPUParticles2D' could not be found (are you missing a using directive or an assembly reference?) /Users/liyihang/Downloads/cursortest/pixpof/src/Skills/Active/IceBolt.cs(120,40)

Cursor:
Analysis error:
1. Error message: CS0246: The type or namespace name 'CPUParticles2D' could not be found
2. Cause: The class name of the particle system in Godot 4.x may have changed
3. Solution:
- Use GPUParticles2D instead of CPUParticles2D
- Or make sure that the relevant namespaces are correctly referenced
4. The particle system implementation in the IceBolt.cs file needs to be modified

Let me fix the particle system error in IceBolt.cs:
...

8 After Cursor is modified, switch to Godot editor, click "Discard local changes and reload", click on the white triangle in the upper right corner to run the game

alt text

9 Click the UI Skill 1 icon or press the "Q" skill shortcut key to trigger the Ice Blast skill

alt text

10 After we develop the code, we need to add automated testing to ensure functional integrity and correctness.

You: Please add unit tests for the Ice Bolt skill and put it in Tests/Unit

Cursor:
I need to create unit tests for the Ice Bolt skill. This test should be placed in the Tests/Unit directory and needs to test the basic functions of Ice Bolt, including:

1. Is the initialization correct?
2. Is the multi-projection function normal?
3. Is the cooldown time correct?
4. Is the mana consumption correct?
5. Is the damage calculation correct?

I will create a new test file: Tests/Unit/Skills/Active/IceBoltTests.cs

...

This unit test file contains comprehensive tests for the Frostbomb skill, including:
Basic property tests (name, description, cooldown, mana cost, etc.)
Enabling and disabling the multi-projection feature
Interaction with auxiliary skills (low-level multi-projection)
Cooldown management
Aiming direction calculation
Verification of constant values ​​for damage and slowdown effects
The test uses the NUnit framework and Moq library to simulate Godot objects, so that unit testing can be performed without relying on the Godot engine. Through the reflection mechanism, we can access and verify the values ​​of private fields to ensure that the internal state is as expected.
It should be noted that these tests focus on the logical functions of Frostbomb, rather than the actual game effects (such as visual effects, sounds, etc.), because these usually need to be verified in integration tests or manual tests.

🎯 Test case generation

// IceBoltTests.cs
using NUnit.Framework;
using Game.Skills.Active;
using Game.Skills.Base;
using Game.Skills.Support;
using Game.Enemies;
using Moq;
using Godot;

namespace Game.Tests.Unit.Skills.Active
{
[TestFixture]
public class IceBoltTests
{
private IceBolt _iceBolt;
private Mock<Node2D> _mockSource;
private Mock<Monster> _mockMonster;

[SetUp]
public void Setup()
{
// 创建寒冰弹实例
_iceBolt = new IceBolt();

// 模拟源节点(玩家)
_mockSource = new Mock<Node2D>();
_mockSource.Setup(s => s.GlobalPosition).Returns(new Vector2(0, 0));
_mockSource.Setup(s => s.GetGlobalMousePosition()).Returns(new Vector2(100, 0));

// 模拟怪物
_mockMonster = new Mock<Monster>();
_mockMonster.Setup(m => m.GlobalPosition).Returns(new Vector2(50, 0));
_mockMonster.Setup(m => m.Name).Returns("TestMonster");
}

[Test]
public void Initialize_ShouldSetCorrectProperties()
{
// Act
_iceBolt.Initialize();

// Assert
Assert.That(_iceBolt.Name, Is.EqualTo("寒冰弹"));
Assert.That(_iceBolt.Description, Is.EqualTo("发射一枚寒冰弹,对敌人造成伤害并减速"));
Assert.That(_iceBolt.Cooldown, Is.EqualTo(2.0f));
Assert.That(_iceBolt.ManaCost, Is.EqualTo(15.0f));
Assert.That(_iceBolt.TriggerType, Is.EqualTo(SkillTriggerType.Active));
}

[Test]
public void EnableMultiProjectiles_ShouldEnableMultishot()
{
// Act
_iceBolt.EnableMultiProjectiles();

// 由于_isMultishot是私有字段,我们需要通过反射来验证它的值
var isMultishotField = _iceBolt.GetType()
.GetField("_isMultishot", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
bool isMultishot = (bool)isMultishotField.GetValue(_iceBolt);

// Assert
Assert.That(isMultishot, Is.True);
}

[Test]
public void DisableMultiProjectiles_ShouldDisableMultishot()
{
// Arrange
_iceBolt.EnableMultiProjectiles();

// Act
_iceBolt.DisableMultiProjectiles();

// 通过反射获取私有字段值
var isMultishotField = _iceBolt.GetType()
.GetField("_isMultishot", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
bool isMultishot = (bool)isMultishotField.GetValue(_iceBolt);

// Assert
Assert.That(isMultishot, Is.False);
}

[Test]
public void AddSupport_WithLesserMultipleProjectiles_ShouldEnableMultishot()
{
// Arrange
var support = new LesserMultipleProjectilesSupport();

// Act
_iceBolt.AddSupport(support);

// 通过反射获取私有字段值
var isMultishotField = _iceBolt.GetType()
.GetField("_isMultishot", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
bool isMultishot = (bool)isMultishotField.GetValue(_iceBolt);

// Assert
Assert.That(isMultishot, Is.True);
}

[Test]
public void Trigger_ShouldStartCooldown()
{
// Arrange
_iceBolt.Initialize();

// Act
_iceBolt.Trigger(_mockSource.Object);

// Assert
Assert.That(_iceBolt.CurrentCooldown, Is.GreaterThan(0));
Assert.That(_iceBolt.CurrentCooldown, Is.EqualTo(_iceBolt.Cooldown));
}

[Test]
public void Update_ShouldReduceCooldown()
{
// Arrange
_iceBolt.Initialize();
_iceBolt.Trigger(_mockSource.Object);
float initialCooldown = _iceBolt.CurrentCooldown;
float deltaTime = 0.5f;

// Act
_iceBolt.Update(deltaTime);

// Assert
Assert.That(_iceBolt.CurrentCooldown, Is.EqualTo(initialCooldown - deltaTime));
}

[Test]
public void CanTrigger_WithZeroCooldown_ShouldReturnTrue()
{
// Arrange
_iceBolt.Initialize();

// Act & Assert
Assert.That(_iceBolt.CanTrigger(), Is.True);
}

[Test]
public void CanTrigger_DuringCooldown_ShouldReturnFalse()
{
// Arrange
_iceBolt.Initialize();
_iceBolt.Trigger(_mockSource.Object);

// Act & Assert
Assert.That(_iceBolt.CanTrigger(), Is.False);
}

[Test]
public void GetAimDirection_ShouldReturnNormalizedDirection()
{
// Arrange
var getAimDirectionMethod = _iceBolt.GetType()
.GetMethod("GetAimDirection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

// Act
Vector2 direction = (Vector2)getAimDirectionMethod.Invoke(_iceBolt, new object[] { _mockSource.Object });

// Assert
Assert.That(direction.Length(), Is.EqualTo(1).Within(0.001f));
Assert.That(direction.X, Is.EqualTo(1).Within(0.001f));
Assert.That(direction.Y, Is.EqualTo(0).Within(0.001f));
}

[Test]
public void IceBolt_DamageConstant_ShouldMatchExpected()
{
// 通过反射获取私有常量值
var damageField = _iceBolt.GetType()
.GetField("DAMAGE", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
float damage = (float)damageField.GetValue(null);

// Assert
Assert.That(damage, Is.EqualTo(20f));
}

[Test]
public void IceBolt_SlowFactorConstant_ShouldMatchExpected()
{
// 通过反射获取私有常量值
var slowFactorField = _iceBolt.GetType()
.GetField("SLOW_FACTOR", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
float slowFactor = (float)slowFactorField.GetValue(null);

// Assert
Assert.That(slowFactor, Is.EqualTo(0.5f));
}

[Test]
public void IceBolt_SlowDurationConstant_ShouldMatchExpected()
{
// 通过反射获取私有常量值
var slowDurationField = _iceBolt.GetType()
.GetField("SLOW_DURATION", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
float slowDuration = (float)slowDurationField.GetValue(null);

// Assert
Assert.That(slowDuration, Is.EqualTo(3.0f));
}
}
}

11 After developing the feature, return to Github Desktop to submit the code

alt text Fill in the summary of the current feature "update IceBolt name", and then click "Commit to IcdBolt"

12 Release the feature branch

Click "Public branch" alt text

13 Create a pull request and submit it to the main repository

Click "Preview Pull Request" to view the code difference alt text

alt text After confirming that the file is correct, click "Create Pull Request" to submit the merge request

14 Then automatically jump to the web page to complete the title and description of the merge request

alt text

Click the "Create pull request" button and wait for the maintainer to review your submission

15 You need to pay attention to the review feedback and make changes based on the comments until the maintainer merges your pull request

alt text

Four debugging skills

📝 We can ask cursor to add log system to view the logs of the output execution process

You: Add logging of ice bullets hitting enemies

Cursor:
Analysis requirements:
1. Need to add logging of ice bullets hitting enemies
2. Need to modify the OnBodyEntered method in the IceBolt.cs file
3. Need to add more detailed log information, including:
- Enemy name
- Damage done to the enemy
- Details of the slowdown effect
- Hit location
4. You can use the GD.Print function to output the log

This modification adds a more detailed hit log, including:
Clear log separators to make the log easier to read
Target type and name
Coordinates of the hit location
Amount of damage caused
Details of the slowdown effect (for monsters)
Enemy's current health status
This allows for clearer tracking of ice bullet hits, making debugging and balancing the game easier.
// src/Skills/Active/IceBolt.cs

GD.Print($"========== Frostbomb hit ==========");
GD.Print($"Target type: Monster ({monster.Name})");
GD.Print($"Hit location: {monster.GlobalPosition}");
GD.Print($"Damage caused: {Damage}");
GD.Print($"Slowdown effect: Reduce {SLOW_FACTOR * 100}% movement speed, lasting {SLOW_DURATION} seconds");
GD.Print($"Current health of monster: {monster.CurrentHealth}/{monster.MaxHealth}");
GD.Print($"==================================");

💡 Practical Tips:

  1. When you don't know how the error occurs, you can ask Cursor to print the logs during execution

🌟 Advanced tips:

  1. Please use the latest claude-3.7-sonnet model to reduce the problems caused by development
  2. Let Cursor review the code for you
  3. Use AI to generate skill special effects parameters
  4. Automatically generate skill documents

Remember: Participating in open source projects is a good way to improve programming skills. Cursor can not only help you write code, but also help you understand other people's code! Let the AI ​​assistant accompany you to explore in the open source world!

Monica AI助手

Monica (莫妮卡)是一款全功能的AI助手,配备了最先进的AI模型,如GPT-4、Claude、Bard等,用于帮助用户聊天、搜索、写作、翻译等多种功能。

立即体验

CursorAI编程知识星球

我正在「史密斯DeepSeekAI社」和朋友们讨论有趣的话题,你⼀起来吧?

立刻查看